#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#ifndef INIT_ARRAY_SIZE
#define INIT_ARRAY_SIZE 16
#endif
#define FALSE 0
#define TRUE !FALSE
#define BOOL char

static struct termios terminal_config;

void load_terminal_config(void) {
    tcgetattr(STDIN_FILENO, &terminal_config);
}

void reset_terminal_config(void) {
    tcsetattr(STDIN_FILENO, TCSANOW, &terminal_config);
}

void set_terminal_raw_mode(void) {
    struct termios temp_terminal_config = terminal_config;
    temp_terminal_config.c_lflag &= ~ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &temp_terminal_config);
}

typedef struct {
    char* value;
    size_t length;
    size_t size;
} String;

typedef struct {
    String name;
    String kernel_makefile;
    String cc;
    String author;
    String license;
    String description;
} Module;

BOOL new_string(String* string) {
    string->value = (char*)malloc(sizeof(char) * INIT_ARRAY_SIZE);
    if (string->value == NULL) {
        perror("\e[91m创建字符串失败\e[0m");
        return FALSE;
    }
    string->length = 0;
    string->size = INIT_ARRAY_SIZE;
    return TRUE;
}

void free_string(String* string) {
    if (string->value != NULL) {
        free(string->value);
        string->value = NULL;
    }
    string->length = 0;
    string->size = 0;
}

void free_module(Module* module) {
    free_string(&module->name);
    free_string(&module->kernel_makefile);
    free_string(&module->cc);
    free_string(&module->author);
    free_string(&module->license);
    free_string(&module->description);
}

BOOL push_string(String* string, char ch) {
    if (string->length == string->size) {
        string->size <<= 1;
        char* new_value = realloc(string->value, string->size);
        if (new_value == NULL) {
            perror("\e[91m分配字符串内存失败");
            free(string->value);
            printf("\e[0m");
            return FALSE;
        }
        string->value = new_value;
    }
    string->value[string->length++] = ch;
    string->value[string->length] = 0;
    return TRUE;
}

int main(int argc, char** argv) {
    load_terminal_config();
    Module module;
    size_t res = new_string(&module.name);
    if (!res) {
        return 1;
    }
    res = new_string(&module.kernel_makefile);
    if (!res) {
        free_string(&module.name);
        return 1;
    }
    res = new_string(&module.cc);
    if (!res) {
        free_module(&module);
        return 1;
    }
    res = new_string(&module.author);
    if (!res) {
        free_module(&module);
        return 1;
    }
    res = new_string(&module.description);
    if (!res) {
        free_module(&module);
        return 1;
    }
    res = new_string(&module.license);
    if (!res) {
        free_module(&module);
        return 1;
    }
    char m_status = 0;
    for (int i = 1; i < argc; i++) {
        for (char* ch = argv[i]; *ch != 0; ch++) {
            switch (m_status) {
                case 0:
                    if (*ch == '-') {
                        m_status = 1;
                    }
                    break;
                case 1:
                    if (*ch == 'n') {
                        m_status = 2;
                    } else {
                        m_status = 0;
                    }
                    break;
                case 2:
                case 3:
                    res = push_string(&module.name, *ch);
                    if (!res) {
                        free_module(&module);
                        return 1;
                    }
                    m_status = 3;
                    break;
            }
        }
        if (m_status == 3) {
            m_status = 0;
        }
    }
    FILE* fp_kernel_cc = fopen("/proc/version", "rt");
    if (fp_kernel_cc == NULL) {
        for (char* ch = "gcc"; *ch != 0; ch++) {
            res = push_string(&module.cc, *ch);
            if (!res) {
                free_module(&module);
                return 1;
            }
        }
    } else {
        char status = 0;
        int ch;
        while ((ch = fgetc(fp_kernel_cc)) != EOF) {
            switch (status) {
            case 0:
                if (ch == '(') {
                    status = 1;
                }
                break;
            case 1:
                if (ch == '(') {
                    status = 2;
                }
                break;
            case 2:
                if (ch != ' ') {
                    res = push_string(&module.cc, ch);
                    if (!res) {
                        free_module(&module);
                        fclose(fp_kernel_cc);
                        return 1;
                    }
                } else {
                    goto final;
                }
                break;
            }
        }
        final: fclose(fp_kernel_cc);
    }
    FILE* fp_kernel_dir = popen("uname -r", "r");
    if (fp_kernel_dir != NULL) {
        for (char* str = "/lib/modules/"; *str !=0; str++) {
            res = push_string(&module.kernel_makefile, *str);
            if (!res) {
                free_module(&module);
                pclose(fp_kernel_dir);
                return 1;
            }
        }
        int ch;
        while ((ch = fgetc(fp_kernel_dir)) != EOF) {
            if (ch == '\r' || ch == '\n') {
                continue;
            }
            res = push_string(&module.kernel_makefile, ch);
            if (!res) {
                free_module(&module);
                pclose(fp_kernel_dir);
                return 1;
            }
        }
        pclose(fp_kernel_dir);
        for (char* str = "/build/"; *str != 0; str++) {
            res = push_string(&module.kernel_makefile, *str);
            if (!res) {
                free_module(&module);
                return 1;
            }
        }
    }
    printf("\e[96m请输入模块名，默认为\e[93m%s：\e[0m", module.name.value);
    BOOL is_new_name = FALSE;
    while (TRUE) {
        int ch = getchar();
        if (ch == EOF || ch == '\n') {
            break;
        } else if (ch == '\n') {
            continue;
        } else {
            if (!is_new_name) {
                module.name.length = 0;
                is_new_name = TRUE;
            }
            res = push_string(&module.name, ch);
            if (!res) {
                free_module(&module);
                return 1;
            }
        }
    }
    printf("\e[96m请输入内核makefile路径，默认\e[93m%s\e[0m：", module.kernel_makefile.value);
    char status = 0;
    while (TRUE) {
        int ch = getchar();
        if (ch == '\r') {
            continue;
        }
        if (ch == '\n' || ch == EOF) {
            break;
        }
        switch (status) {
        case 0:
            module.kernel_makefile.length = 0;
            status = 1;
            res = push_string(&module.kernel_makefile, ch);
            if (!res) {
                free_module(&module);
                return 1;
            }
            break;
        case 1:
            res = push_string(&module.kernel_makefile, ch);
            if (!res) {
                free_module(&module);
                return 1;
            }
            break;
        }
    }
    printf("\e[96m是否生成可加载模块，默认为\e[93my\e[0m[y/n]：");
    set_terminal_raw_mode();
    int ch = getchar();
    reset_terminal_config();
    char can_load[6] = { "obj-m" };
    if (ch == 'n' || ch == 'N') {
        can_load[4] = 'y';
    }
    printf("\e[96m请输入模块作者，默认为\e[93m空\e[0m：");
    while (TRUE) {
        int ch = getchar();
        if (ch == '\n' || ch == EOF) {
            break;
        }
        if (ch == '\r') {
            continue;
        }
        res = push_string(&module.author, ch);
        if (!res) {
            free_module(&module);
            return 1;
        }
    }
    printf("\e[96m请输入模块描述，默认为\e[93m空\e[0m：");
    while (TRUE) {
        int ch = getchar();
        if (ch == '\n' || ch == EOF) {
            break;
        }
        if (ch == '\r') {
            continue;
        }
        res = push_string(&module.description, ch);
        if (!res) {
            free_module(&module);
            return 1;
        }
    }
    printf("\e[96m请输入模块许可证，可用的许可证如下：\n\e[95m\
    1. GPL\n\
    2. GPL v2\n\
    3. GPL and additional rights\n\
    4. Dual BSD/GPL\n\
    5. Dual MPL/GPL\n\
    6. Proprietary\n\
\e[96m请输入许可证编号，默认为\e[93m2\e[0m：");
    set_terminal_raw_mode();
    ch = getchar();
    reset_terminal_config();
    switch (ch) {
        case '1':
            for (char* ch = "GPL"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
        case '3':
            for (char* ch = "GPL and additional rights"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
        case '4':
            for (char* ch = "Dual BSD/GPL"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
        case '5':
            for (char* ch = "Dual MPL/GPL"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
        case '6':
            for (char* ch = "Proprietary"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
        default:
            for (char* ch = "GPL v2"; *ch != 0; ch++) {
                res = push_string(&module.license, *ch);
                if (!res) {
                    free_module(&module);
                    return 1;
                }
            }
            break;
    }
    printf("\n\e[96m正在创建%s项目\e[0m\n", module.name.value);
    if (access(module.name.value, F_OK) != 0) {
        if (mkdir(module.name.value, S_IRWXU | S_IRGRP | S_IXOTH) != 0) {
            perror("\e[91m创建项目失败");
            free_module(&module);
            printf("\e[0m");
            return 1;
        }
    }
    char makefile_path[module.name.length + 10];
    sprintf(makefile_path, "%s/Makefile", module.name.value);
    FILE* fp = fopen(makefile_path, "wb");
    if (fp == NULL) {
        perror("\e[91m创建项目makefile失败");
        free_module(&module);
        printf("\e[0m");
        return 1;
    }
    char makefile_content[400 + module.name.length + module.kernel_makefile.length];
    sprintf(makefile_content,
"%s = %s.o\n\
CC = %s\n\
kernel_dir = %s\n\
pwd = $(shell pwd)\n\
all:\n\
\tmake CC=$(CC) -C $(kernel_dir) M=$(pwd) modules\n\
clean:\n\
\t@rm -rf *.ko *.o *.symvers *.cmd *.cmd,o *.order *.mod*",
    can_load, module.name.value, module.cc.value, module.kernel_makefile.value);
    size_t makefile_length = strlen(makefile_content);
    res = fwrite(makefile_content, sizeof(char), makefile_length, fp);
    if (res != makefile_length) {
        perror("\e[91m写入项目makefile失败\e[0m");
    }
    fclose(fp);
    char mainfile_path[module.name.length * 2 + 4];
    sprintf(mainfile_path, "%s/%s.c", module.name.value, module.name.value);
    fp = fopen(mainfile_path, "wb");
    if (fp == NULL) {
        perror("\e[91m创建项目C代码文件失败");
        free_module(&module);
        printf("\e[0m");
        return 1;
    }
    char mainfile_content[400 + module.author.length + module.license.length + module.description.length];
    sprintf(mainfile_content,
"#include <linux/module.h>\n\
#include <linux/kernel.h>\n\
\n\
static int __init init_func(void) {\n\
    return 0;\n\
}\n\
\n\
static void __exit exit_func(void) {\n\
}\n\
\n\
module_init(init_func);\n\
module_exit(exit_func);\n\
MODULE_AUTHOR(\"%s\");\n\
MODULE_LICENSE(\"%s\");\n\
MODULE_DESCRIPTION(\"%s\");",
    module.author.value, module.license.value, module.description.value);
    size_t mainfile_length = strlen(mainfile_content);
    res = fwrite(mainfile_content, sizeof(char), mainfile_length, fp);
    if (res != mainfile_length) {
        perror("\e[91m写入项目C代码文件失败\e[0m");
    }
    fclose(fp);
    free_module(&module);
    puts("\e[96m创建项目成功\e[0m");
    return 0;
}